@tool
class_name VisualView
extends Panel

const node_dict = {
	GameEnum.SkillNodeType.Skill : {
		Skill = {
			ActionScript = preload("res://scripts/system/skill/Skill.gd"),
			NodeScene = preload("res://addons/Skill_Editor/scenes/skill_node.tscn"),
		},
	},
	GameEnum.SkillNodeType.Control : {
		Skill_Control_Sequence = {
			ActionScript = preload("res://scripts/system/skill/control/Skill_Control_Sequence.gd"),
			NodeScene = preload("res://addons/Skill_Editor/scenes/skill_action_node.tscn"),
		},
		Skill_Action_SearchTarget = {
			ActionScript = preload("res://scripts/system/skill/action/Skill_Action_SearchTarget.gd"),
			NodeScene = preload("res://addons/Skill_Editor/scenes/skill_action_node.tscn"),
		},
		Skill_Action_AddBuff = {
			ActionScript = preload("res://scripts/system/skill/action/Skill_Action_AddBuff.gd"),
			NodeScene = preload("res://addons/Skill_Editor/scenes/skill_action_node.tscn"),
		},
	},
}
#region 属性
@export var skill_list:ItemList
@export var graph_edit:GraphEdit
@export var action_tree:Tree
@export var no_skill_lab:Label
@export var global_blackboard:Control
@export var property_list:ScrollContainer
@export var property_list_container:VBoxContainer
@export_group("Button")
@export var clean_btn:Button
@export var add_property_btn:Button
@export var lock_btn:Button
@export var unlock_btn:Button
@export_group("")
@export_group("Scene")
@export var property_box_scene:PackedScene = load("res://addons/Skill_Editor/scenes/property_box.tscn")
@export var property_info_box_scene:PackedScene = load("res://addons/Skill_Editor/scenes/property_info_box.tscn")
@export_group("")
var skills:Array[Skill]
var cur_skill:Skill
var cur_node:NodeBase
var connection_obj_dict:Dictionary[Signal,ConnectObj] = {}
var auto_connection_info = {
	node_name = "", # ""代表没有自动连接的对象
	node_port = -1, # -1代表有自动连接的端口
	node_position = Vector2.ZERO,
}
var node_info_dict:Dictionary[String,Dictionary] = {}
var cur_node_dict:Dictionary[StringName,NodeBase] = {}
var is_locked:bool = false
var cur_node_id:int = 0
#endregion
#region 字段
#endregion
#region 信号
signal property_added()
signal property_removed(property_name:String)
signal property_name_changed()
signal property_type_changed()
signal property_value_changed(value)
#endregion

#region 基类方法
#endregion
#region 公共方法
func get_cur_skill() -> Skill:
	return cur_skill
func get_root_node() -> NodeBase:
	var root_node:NodeBase = _get_node_from_node_name("Root")
	return root_node
#endregion
#region 私有方法
func _clear_all():
	_hide_node_list()
	_clean_useless_node()
	for node in graph_edit.get_children():
		if node is GraphNode:
			node.free()
	for child in self.property_list_container.get_children():
		child.free()
	cur_node_id = 0
	_deselect_node(cur_node)
	cur_skill = null
func _get_node_from_node_name(node_name:String) -> NodeBase:
	return cur_node_dict.get(node_name,null)
func _get_next_node_id(action) -> String:
	var id = "@" + str(action.get_instance_id())
	cur_node_id += 1
	return id
func _connect_node(from_node:NodeBase,from_port:int,to_node:NodeBase,to_port:int) -> bool:
	if not from_node.check_can_connect_children(to_node) or not to_node.check_can_connect_parent(from_node):
		return false
	from_node.connect_children(from_port,to_node)
	to_node.connect_parent(to_port,from_node)
	graph_edit.connect_node(from_node.name,from_port,to_node.name,to_port)
	return true
func _disconnect_node(from_node:NodeBase,from_port:int,to_node:NodeBase,to_port:int) -> bool:
	if graph_edit.get_connection_list().filter(func (item):
		return item.from_node==from_node.name and item.from_port==from_port and item.to_node==to_node.name and item.to_port==to_port
		).size()<=0:
		return false
	from_node.disconnect_children(from_port,to_node)
	to_node.disconnect_parent(to_port,from_node)
	graph_edit.disconnect_node(from_node.name,from_port,to_node.name,to_port)
	return true
func _select_node(node:NodeBase):
	if cur_node == node:
		return
	_deselect_node(cur_node)
	cur_node = node
	EditorInterface.edit_node(cur_node.target)
func _deselect_node(node:NodeBase):
	if cur_node == node:
		cur_node = null
		EditorInterface.edit_node(null)
## 判断是否是Root节点的后代
func _check_descendant_of_root_node(node:NodeBase) -> bool:
	var root_node:NodeBase = _get_node_from_node_name("Root")
	var parent_list = []
	parent_list.append_array(node.parent_nodes)
	while parent_list.size() > 0:
		var parent = parent_list.pop_front()
		if parent.name == root_node.name:
			return true
		parent_list.append_array(parent.parent_nodes)
	return false
## 清理无效的Action
func _clean_useless_node():
	if not cur_skill:
		return
	var node_names = [] as Array[StringName]
	var root_node = _get_node_from_node_name("Root")
	for node:NodeBase in cur_node_dict.values():
		if node.name != root_node.name and not _check_descendant_of_root_node(node):
			node_names.append(node.name)
	if node_names.size() > 0:
		_on_delete_nodes_request(node_names)
#region 实例化相关
## 创建Action
func _create_action(action_real_name:String,parent:Node = null) -> Node2D:
	var action = Node2D.new()
	var info = node_info_dict.get(action_real_name)
	if not info or not info.ActionScript:
		return null
	action.set_script(info.ActionScript)
	if parent:
		parent.add_child(action)
	elif cur_skill:
		cur_skill.add_child(action)
	action.owner = parent.owner if parent else cur_skill
	return action
## 创建SkillNode
func _create_skill_node(skill:Skill,pos = null) -> SkillNodeBase:
	var action_real_name = skill.get_script().get_global_name()
	var info = node_info_dict.get(action_real_name)
	if not info or not info.NodeScene:
		return null
	var node:SkillNodeBase = info.NodeScene.instantiate()
	node.name = _get_next_node_id(skill)
	node.init_node(self,skill,{IsStartSkill = skill == self.cur_skill})
	graph_edit.add_child(node)
	node.owner = self
	if pos:
		node.position_offset = pos
	if auto_connection_info.node_name != "" and auto_connection_info.node_port != -1:
		var from_node:NodeBase = _get_node_from_node_name(auto_connection_info.node_name)
		var to_node:NodeBase = _get_node_from_node_name(node.name)
		if from_node and to_node:
			_connect_node(from_node,auto_connection_info.node_port,to_node,0)
	return node
## 创建ActionNode
func _create_action_node(action:SkillAction,pos = null) -> ActionNodeBase:
	var action_real_name = action.get_script().get_global_name()
	var info = node_info_dict.get(action_real_name)
	if not info or not info.NodeScene:
		return null
	var node:ActionNodeBase = info.NodeScene.instantiate()
	node.name = _get_next_node_id(action)
	node.init_node(self,action)
	graph_edit.add_child(node)
	node.owner = self
	if pos:
		node.position_offset = pos
	if auto_connection_info.node_name != "" and auto_connection_info.node_port != -1:
		var from_node:NodeBase = _get_node_from_node_name(auto_connection_info.node_name)
		var to_node:NodeBase = _get_node_from_node_name(node.name)
		if from_node and to_node:
			_connect_node(from_node,auto_connection_info.node_port,to_node,0)
	return node
## 创建PropertyInfoBox
func _create_property_info_box() -> PropertyInfoBox:
	var property_info_box:PropertyInfoBox = property_info_box_scene.instantiate() as PropertyInfoBox
	property_list_container.add_child(property_info_box)
	return property_info_box
#endregion
#region 创建列表相关
## 过滤创建列表
func _filter_node_list(allow_node_types = null):
	var item:TreeItem = action_tree.get_root().get_child(0)
	while item:
		item.visible = true if allow_node_types == null else GameEnum.SkillNodeType[item.get_text(0)] in allow_node_types
		item = item.get_next()
## 填充创建列表
func _fill_node_list():
	var root = action_tree.create_item()
	for type in node_dict.keys():
		var info_dict = node_dict[type]
		var type_item = action_tree.create_item(root)
		var type_name = Utils.get_enum_key_by_value(GameEnum.SkillNodeType,type)
		type_item.set_text(0,type_name)
		for item_name in info_dict.keys():
			var item = action_tree.create_item(type_item)
			item.set_text(0,item_name)
			node_info_dict[item_name] = info_dict[item_name]
## 显示创建列表
func _show_node_list(node:NodeBase = null,port:int = -1,node_position:Vector2 = Vector2.ZERO):
	action_tree.position = graph_edit.get_local_mouse_position()
	action_tree.visible = true
	var root_node = _get_node_from_node_name("Root")
	var pos = graph_edit.get_local_mouse_position() / root_node.scale - root_node.position / root_node.scale
	if node and port != -1:
		var allow_node_types = node.children_port_allow_types
		_filter_node_list(allow_node_types)
		_set_auto_connection_info(node.name,port,pos)
	else:
		_filter_node_list()
		_set_auto_connection_info("",0,pos)
## 隐藏创建列表
func _hide_node_list():
	action_tree.visible = false
	_set_auto_connection_info("",0,Vector2.ZERO)
func _set_auto_connection_info(node_name,node_port,pos):
	auto_connection_info.node_name = node_name
	auto_connection_info.node_port = node_port
	auto_connection_info.node_position = pos
#endregion
#region 更新相关
## 更新Skill列表
func _update_skill_list():
	skill_list.clear()
	for i in range(skills.size()):
		skill_list.add_item(skills[i].skill_name)
		if self.cur_skill == skills[i]:
			skill_list.select(i)
## 更新全局属性列表
func _update_property_list():
	if not cur_skill:
		return
	for child in self.property_list_container.get_children():
		child.queue_free()
	for key in self.cur_skill.init_data.keys():
		var property_info_box:PropertyInfoBox = _create_property_info_box() as PropertyInfoBox
		property_info_box.init(self,self.cur_skill,key)
## 更新主视图
func _update_graph_edit():
	if not self.cur_skill:
		return
	var queue = []
	# 第一个属性为深度
	queue.append([0,self.cur_skill,null])
	while queue.size() > 0:
		var item = queue.pop_front()
		var depth = item[0]
		var skill_action = item[1]
		var parent_node:NodeBase = item[2]
		if skill_action is SkillComposite:
			var node = _create_action_node(skill_action,Vector2.ZERO)
			if not node:
				return
			cur_node_dict.set(node.name,node)
			if parent_node:
				_connect_node(parent_node,0,node,0)
			for sub_action in skill_action.sub_actions:
				queue.push_back([depth + 1,sub_action,node])
		elif skill_action is SkillControl:
			var node = _create_action_node(skill_action,Vector2.ZERO)
			if not node:
				return
			cur_node_dict.set(node.name,node)
			if parent_node:
				_connect_node(parent_node,0,node,0)
			for sub_action in skill_action.sub_actions:
				queue.push_back([depth + 1,sub_action,node])
		elif skill_action is SkillAction:
			var node = _create_action_node(skill_action,Vector2.ZERO)
			if not node:
				return
			cur_node_dict.set(node.name,node)
			if parent_node:
				_connect_node(parent_node,0,node,0)
		elif skill_action is Skill:
			var node = _create_skill_node(skill_action,Vector2.ZERO)
			if not node:
				return
			cur_node_dict.set(node.name,node)
			if not cur_node_dict.get("Root"):
				cur_node_dict.set("Root",node)
			if parent_node:
				_connect_node(parent_node,0,node,0)
			if skill_action.action:
				queue.push_back([depth + 1,skill_action.action,node])
	# 创建所有连接
	# 第一个属性为深度
	var root:NodeBase = get_root_node()
	if not root:
		return
	queue.append([0,root])
	while queue.size() > 0:
		var item = queue.pop_front()
		var depth = item[0]
		var node:NodeBase = item[1]
		for child:NodeBase in node.children_nodes:
			queue.push_back([depth + 1,child])
	# 自动排列
	_auto_arrange()
## 更新Lock按钮
func _update_lock_unlock():
	lock_btn.visible = not is_locked
	unlock_btn.visible = is_locked
## 自动排列
func _auto_arrange():
	var root:NodeBase = get_root_node()
	if not root:
		return
	var queue = []
	var max_width_dict = {}
	# 第一个属性为深度
	queue.append([0,root])
	while queue.size() > 0:
		var item = queue.pop_front()
		var depth = item[0]
		var node:NodeBase = item[1]
		var rect:Rect2 = node.get_global_rect()
		if not max_width_dict.has(depth):
			max_width_dict.set(depth,0)
		max_width_dict.set(depth,max(max_width_dict.get(depth,0),rect.size.x / node.scale.x))
		var height = 0
		for child:NodeBase in node.children_nodes:
			child.offest = Vector2(SkillEditorContants.AUTO_ARRANGE_SPAN_WIDTH,height)
			height += child.get_global_rect().size.y / child.scale.x + SkillEditorContants.AUTO_ARRANGE_SPAN_HEIGHT
		height -= 0 if height <= 0 else SkillEditorContants.AUTO_ARRANGE_SPAN_HEIGHT
		for child:NodeBase in node.children_nodes:
			child.offest += Vector2(0,-height/2 + rect.size.y / 2 / node.scale.y)
			queue.push_back([depth + 1,child])
	# 第一个属性为深度
	queue.append([0,root])
	while queue.size() > 0:
		var item = queue.pop_front()
		var depth = item[0]
		var node:NodeBase = item[1]
		var rect:Rect2 = node.get_global_rect()
		if max_width_dict.get(depth - 1,null):
			node.offest += Vector2(max_width_dict.get(depth - 1,0),0)
		for child:NodeBase in node.children_nodes:
			queue.push_back([depth + 1,child])
	# 第一个属性为深度
	queue.append([0,root])
	while queue.size() > 0:
		var item = queue.pop_front()
		var depth = item[0]
		var node:NodeBase = item[1]
		node.position_offset = (Vector2.ZERO if node.parent_nodes.size() < 1 else node.parent_nodes[0].position_offset) + node.offest
		for child:NodeBase in node.children_nodes:
			queue.push_back([depth + 1,child])
#endregion
#region 信号回调
func _on_selection_changed():
	if is_locked:
		return
	var selection:EditorSelection = EditorInterface.get_selection()
	var nodes = selection.get_selected_nodes()
	skills.clear()
	for n in nodes:
		if n is Skill:
			skills.append(n as Skill)
	_update_skill_list()
	if cur_skill in skills:
		return
	_clear_all()
func _on_skill_list_selected(index:int):
	if index < skills.size():
		self.cur_skill = skills[index]
		_update_graph_edit()
		_update_property_list()
func _on_skill_action_list_item_selected():
	var selected = action_tree.get_selected()
	#if selected.get_child_count() > 0:
		#selected.set_collapsed_recursive(not action_tree.get_selected().collapsed)
		#action_tree.deselect_all()
		#return
	var item_name = selected.get_text(0)
	var is_auto = auto_connection_info.node_name != "" and auto_connection_info.node_port != -1
	var auto_parent_action = _get_node_from_node_name(auto_connection_info.node_name) 
	var action = _create_action(item_name, auto_parent_action.target if auto_parent_action else null)
	if not action:
		return
	var root_node:NodeBase = get_root_node()
	if not root_node:
		return 
	var pos = auto_connection_info.node_position
	var node
	if action is SkillAction:
		node = _create_action_node(action,pos)
	elif action is Skill:
		node = _create_skill_node(action,pos)
	if node:
		cur_node_dict.set(node.name,node)
		_select_node(node)
		if is_auto and auto_parent_action:
			_connect_node(auto_parent_action,0,node,0)
	_hide_node_list()
## 鼠标点击非item_tree的子项时
func _on_skill_action_list_nothing_selected():
	_hide_node_list()
## 锁定
func _on_lock_btn_pressed():
	self.is_locked = true
	_update_lock_unlock()
## 解锁
func _on_unlock_btn_pressed():
	self.is_locked = false
	_update_lock_unlock()
## 清理Skill
func _on_clean_btn_pressed():
	_clean_useless_node()
## 新增Property
func _on_add_property_btn_pressed():
	var property_name = "@" + str(Time.get_ticks_msec())
	self.cur_skill.init_data.set(property_name,"")
	var property_info_box:PropertyInfoBox = _create_property_info_box() as PropertyInfoBox
	property_info_box.init(self,self.cur_skill,property_name)
	property_added.emit()
#region 请求
## 连接请求
func _on_connection_request(from_node_name:StringName,from_port:int,to_node_name:StringName,to_port:int):
	var from_node:NodeBase = _get_node_from_node_name(from_node_name)
	var to_node:NodeBase = _get_node_from_node_name(to_node_name)
	if from_node and to_node:
		_connect_node(from_node,from_port,to_node,to_port)
## 断开连接请求
func _on_disconnection_request(from_node_name:StringName,from_port:int,to_node_name:StringName,to_port:int):
	var from_node:NodeBase = _get_node_from_node_name(from_node_name)
	var to_node:NodeBase = _get_node_from_node_name(to_node_name)
	if from_node and to_node:
		_disconnect_node(from_node,from_port,to_node,to_port)
## 连接到空
func _on_connection_to_empty(from_node_name: StringName, from_port: int, release_position: Vector2):
	var from_node:NodeBase = _get_node_from_node_name(from_node_name)
	var root_node:NodeBase = _get_node_from_node_name("Root")
	if from_node and root_node:
		_show_node_list(from_node,from_port,release_position / root_node.scale - root_node.position / root_node.scale)
## 删除请求
func _on_delete_nodes_request(node_names: Array[StringName]):
	var root_node = _get_node_from_node_name("Root")
	for node_name in node_names:
		if root_node and node_name == root_node.name:
			continue
		for connection in graph_edit.get_connection_list():
			if connection.from_node == node_name or connection.to_node == node_name:
				_on_disconnection_request(connection.from_node,connection.from_port,connection.to_node,connection.to_port)
		var node:NodeBase = _get_node_from_node_name(node_name)
		if node:
			_deselect_node(node)
			cur_node_dict.erase(node.name)
			node.destroy()
## Ctrl+D请求
func _on_duplicate_nodes_request():
	# 处理Ctrl+D请求
	var action:SkillAction = cur_node.action
	var action_copy:SkillAction = action.duplicate()
	action_copy.owner = action.owner
	action_copy.name = action.name + "_" + str(Time.get_ticks_msec())
	action.get_parent().add_child(action_copy)
	var node_copy = _create_action_node(action,cur_node.position_offset + SkillEditorContants.DUPLICATE_OFFEST)
	for connection in graph_edit.get_connection_list():
		if connection.from_node == cur_node.name:
			_on_connection_request(node_copy.name,connection.from_port,connection.to_node,connection.to_port)
		elif connection.to_node == cur_node.name:
			_on_connection_request(connection.from_node,connection.from_port,node_copy.name,connection.to_port)
	_select_node(node_copy)
## 当请求弹出窗口时发出
func _on_popup_request(at_position: Vector2):
	if not action_tree.visible:
		_show_node_list()
	elif action_tree.visible:
		_hide_node_list()
## ActionNode节点被选中
func _on_node_selected(node:NodeBase):
	_select_node(node)
## ActionNode节点失去选中
func _on_node_deselected(node:NodeBase):
	_deselect_node(node)
## 自动排列按钮被点击
func _on_auto_arrange_btn_pressed():
	_auto_arrange()
#endregion
#endregion
#endregion
#region 生命周期函数
func _ready() -> void:
	connection_obj_dict[EditorInterface.get_selection().selection_changed] = ConnectObj.new(EditorInterface.get_selection().selection_changed,[_on_selection_changed])
	connection_obj_dict[skill_list.item_selected] = ConnectObj.new(skill_list.item_selected,[_on_skill_list_selected])
	connection_obj_dict[action_tree.item_selected] = ConnectObj.new(action_tree.item_selected,[_on_skill_action_list_item_selected])
	connection_obj_dict[action_tree.nothing_selected] = ConnectObj.new(action_tree.nothing_selected,[_on_skill_action_list_nothing_selected])
	connection_obj_dict[graph_edit.connection_request] = ConnectObj.new(graph_edit.connection_request,[_on_connection_request])
	connection_obj_dict[graph_edit.disconnection_request] = ConnectObj.new(graph_edit.disconnection_request,[_on_disconnection_request])
	connection_obj_dict[graph_edit.connection_to_empty] = ConnectObj.new(graph_edit.connection_to_empty,[_on_connection_to_empty])
	connection_obj_dict[graph_edit.delete_nodes_request] = ConnectObj.new(graph_edit.delete_nodes_request,[_on_delete_nodes_request])
	connection_obj_dict[graph_edit.duplicate_nodes_request] = ConnectObj.new(graph_edit.duplicate_nodes_request,[_on_duplicate_nodes_request])
	connection_obj_dict[graph_edit.popup_request] = ConnectObj.new(graph_edit.popup_request,[_on_popup_request])
	connection_obj_dict[graph_edit.node_selected] = ConnectObj.new(graph_edit.node_selected,[_on_node_selected])
	connection_obj_dict[graph_edit.node_deselected] = ConnectObj.new(graph_edit.node_deselected,[_on_node_deselected])
	
	connection_obj_dict[lock_btn.pressed] = ConnectObj.new(lock_btn.pressed,[_on_lock_btn_pressed])
	connection_obj_dict[unlock_btn.pressed] = ConnectObj.new(unlock_btn.pressed,[_on_unlock_btn_pressed])
	connection_obj_dict[clean_btn.pressed] = ConnectObj.new(clean_btn.pressed,[_on_clean_btn_pressed])
	connection_obj_dict[add_property_btn.pressed] = ConnectObj.new(add_property_btn.pressed,[_on_add_property_btn_pressed])
	
	# 重新绑定原本的自动排列按钮点击信号
	var auto_arrange_btn:Button = graph_edit.get_menu_hbox().get_child(-1) as Button
	for info in auto_arrange_btn.get_signal_connection_list("pressed"):
		auto_arrange_btn.pressed.disconnect(info.callable)
	auto_arrange_btn.pressed.connect(_on_auto_arrange_btn_pressed)
	_fill_node_list()
	_update_lock_unlock()
func _process(delta: float) -> void:
	pass
func _input(event: InputEvent) -> void:
	if event is InputEventMouseButton:
		event = event as InputEventMouseButton
		if event.pressed and action_tree.visible and not action_tree.get_rect().has_point(graph_edit.get_local_mouse_position()):
			_hide_node_list.call_deferred()
func _exit_tree() -> void:
	for connection_obj:ConnectObj in connection_obj_dict.values():
		if connection_obj:
			connection_obj.disconnect_all()
	_clear_all()
#endregion
